﻿using System;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Windows.Media;
using System.Windows;
using System.Windows.Documents;

namespace GrapeCity.Win32
{
    public class FontDialog : CommonDialog
    {
        private Color color;
        private const int defaultMaxSize = 0;
        private const int defaultMinSize = 0;
        private int maxSize;
        private int minSize;
        private int options;
        private bool showColor;
        private FontWrapper font;

        public FontDialog()
        {
            this.Reset();
        }

        internal void SetOption(int option, bool value)
        {
            if (value)
                this.options |= option;
            else
                this.options &= ~option;
        }

        internal bool GetOption(int option)
        {
            return ((this.options & option) != 0);
        }

        public override void Reset()
        {
            this.options = 0x101;
            if (object.ReferenceEquals(this.font, null))
                this.font = new FontWrapper();
            else
                this.font.Reset();
            this.color = Colors.Black;
            this.showColor = false;
            this.minSize = 0;
            this.maxSize = 0;
            this.SetOption(0x40000, true);
        }

        private void UpdateColor(int rgb)
        {
            if (this.color.ToWin32() != rgb)
                this.color = rgb.ToOleColor();
        }

        protected override bool RunDialog(IntPtr hwndOwner)
        {
            bool flag;
            NativeMethods.WndProc proc = new NativeMethods.WndProc(this.HookProc);
            NativeMethods.CHOOSEFONT cf = new NativeMethods.CHOOSEFONT();
            NativeMethods.LOGFONT logFont = this.font.ToLogFont();
            IntPtr zero = IntPtr.Zero;
            try
            {
                zero = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NativeMethods.LOGFONT)));
                Marshal.StructureToPtr(logFont, zero, false);
                cf.lStructSize = Marshal.SizeOf(typeof(NativeMethods.CHOOSEFONT));
                cf.hwndOwner = hwndOwner;
                cf.hDC = IntPtr.Zero;
                cf.lpLogFont = zero;
                cf.Flags = this.Options | 0x40 | 8;
                if (this.minSize > 0 || this.maxSize > 0) cf.Flags |= 0x2000;
                if (this.ShowColor || this.ShowEffects)
                    cf.rgbColors = this.color.ToWin32();
                else
                    cf.rgbColors = Colors.Black.ToWin32();
                cf.lpfnHook = proc;
                cf.hInstance = UnsafeNativeMethods.GetModuleHandle(null);
                cf.nSizeMin = this.minSize;
                if (this.maxSize == 0)
                    cf.nSizeMax = 0x7fffffff;
                else
                    cf.nSizeMax = this.maxSize;
                if (!SafeNativeMethods.ChooseFont(cf)) return false;
                NativeMethods.LOGFONT logfont2 = null;
                logfont2 = (NativeMethods.LOGFONT)Marshal.PtrToStructure(zero, typeof(NativeMethods.LOGFONT));
                if (logfont2.lfFaceName != null && logfont2.lfFaceName.Length > 0)
                {
                    logFont = logfont2;
                    this.UpdateFont(logFont);
                    this.UpdateColor(cf.rgbColors);
                }
                flag = true;
            }
            finally
            {
                if (zero != IntPtr.Zero) Marshal.FreeCoTaskMem(zero);
            }
            return flag;

        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override IntPtr HookProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
        {
            switch (msg)
            {
                case 0x110:
                    if (!this.showColor)
                    {
                        IntPtr dlgItem = UnsafeNativeMethods.GetDlgItem(new HandleRef(null, hWnd), 0x473);
                        SafeNativeMethods.ShowWindow(new HandleRef(null, dlgItem), 0);
                        dlgItem = UnsafeNativeMethods.GetDlgItem(new HandleRef(null, hWnd), 0x443);
                        SafeNativeMethods.ShowWindow(new HandleRef(null, dlgItem), 0);
                    }
                    break;

                case 0x111:
                    if (((int)wparam) == 0x402)
                    {
                        NativeMethods.LOGFONT lParam = new NativeMethods.LOGFONT();
                        UnsafeNativeMethods.SendMessage(new HandleRef(null, hWnd), 0x401, 0, lParam);
                        this.UpdateFont(lParam);
                        int num = (int)UnsafeNativeMethods.SendDlgItemMessage(new HandleRef(null, hWnd), 0x473, 0x147, IntPtr.Zero, IntPtr.Zero);
                        if (num != -1) this.UpdateColor((int)UnsafeNativeMethods.SendDlgItemMessage(new HandleRef(null, hWnd), 0x473, 0x150, (IntPtr)num, IntPtr.Zero));

                        //
                        this.OnApply(EventArgs.Empty);
                    }
                    break;
            }
            return base.HookProc(hWnd, msg, wparam, lparam);
        }

        protected virtual void OnApply(EventArgs e)
        {
            if (Apply != null)
                Apply(this, e);
        }

        private void UpdateFont(NativeMethods.LOGFONT lf)
        {
            this.font.Update(lf);
        }

        public event EventHandler Apply = null;

        public FontWrapper Font
        {
            get { return font; }
            set { font = value ?? new FontWrapper(); }
        }

        public bool AllowScriptChange
        {
            get { return !this.GetOption(0x400000); }
            set { this.SetOption(0x400000, !value); }
        }

        public bool AllowSimulations
        {
            get { return !this.GetOption(0x1000); }
            set { this.SetOption(0x1000, !value); }
        }

        public bool AllowVectorFonts
        {
            get { return !this.GetOption(0x800); }
            set { this.SetOption(0x800, !value); }
        }

        public bool AllowVerticalFonts
        {
            get { return !this.GetOption(0x1000000); }
            set { this.SetOption(0x1000000, !value); }
        }

        public Color Color
        {
            get { return this.color; }
            set { this.color = value.IsEmpty() ? Colors.Black : value; }
        }

        public bool FixedPitchOnly
        {
            get { return this.GetOption(0x4000); }
            set { this.SetOption(0x4000, value); }
        }

        public bool FontMustExist
        {
            get { return this.GetOption(0x10000); }
            set { this.SetOption(0x10000, value); }
        }

        public int MaxSize
        {
            get { return this.maxSize; }
            set
            {
                if (value < 0x0) value = 0x0;
                this.maxSize = value;
                if (this.maxSize > 0x0 && this.maxSize < this.minSize) this.minSize = this.maxSize;
            }
        }

        public int MinSize
        {
            get { return this.minSize; }
            set
            {
                if (value < 0x0) value = 0x0;
                this.minSize = value;
                if (this.maxSize > 0x0 && this.maxSize < this.minSize) this.maxSize = this.minSize;
            }
        }

        protected int Options
        {
            get { return this.options; }
        }

        public bool ScriptsOnly
        {
            get { return this.GetOption(0x400); }
            set { this.SetOption(0x400, value); }
        }

        public bool ShowApply
        {
            get { return this.GetOption(0x200); }
            set { this.SetOption(0x200, value); }
        }

        public bool ShowColor
        {
            get { return this.showColor; }
            set { this.showColor = value; }
        }

        public bool ShowEffects
        {
            get { return this.GetOption(0x100); }
            set { this.SetOption(0x100, value); }
        }

        public bool ShowHelp
        {
            get { return this.GetOption(0x4); }
            set { this.SetOption(0x4, value); }
        }
    }

    public class FontWrapper : DependencyObject
    {
        public static readonly DependencyProperty FontFamilyProperty;
        public static readonly DependencyProperty FontSizeProperty;
        public static readonly DependencyProperty FontStretchProperty;
        public static readonly DependencyProperty FontStyleProperty;
        public static readonly DependencyProperty FontWeightProperty;

        static FontWrapper()
        {
            Type typeofThis = typeof(FontWrapper);

            FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeofThis,
                new FrameworkPropertyMetadata(SystemFonts.MessageFontFamily, FrameworkPropertyMetadataOptions.Inherits));
            FontSizeProperty = TextElement.FontSizeProperty.AddOwner(typeofThis,
                new FrameworkPropertyMetadata(SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits));
            FontStretchProperty = TextElement.FontStretchProperty.AddOwner(typeofThis,
                new FrameworkPropertyMetadata(TextElement.FontStretchProperty.DefaultMetadata.DefaultValue, FrameworkPropertyMetadataOptions.Inherits));
            FontStyleProperty = TextElement.FontStyleProperty.AddOwner(typeofThis,
                new FrameworkPropertyMetadata(SystemFonts.MessageFontStyle, FrameworkPropertyMetadataOptions.Inherits));
            FontWeightProperty = TextElement.FontWeightProperty.AddOwner(typeofThis,
                new FrameworkPropertyMetadata(SystemFonts.MessageFontWeight, FrameworkPropertyMetadataOptions.Inherits));
        }

        public FontWrapper()
        {
        }

        public FontWrapper(FontFamily family, double size, FontStretch stretch, FontStyle style, FontWeight weight)
        {
            this.SetCurrentValue(FontFamilyProperty, family);
            this.SetCurrentValue(FontSizeProperty, size);
            this.SetCurrentValue(FontStretchProperty, stretch);
            this.SetCurrentValue(FontStyleProperty, style);
            this.SetCurrentValue(FontWeightProperty, weight);
        }

        public FontFamily FontFamily
        {
            get { return (FontFamily)GetValue(FontFamilyProperty); }
            set { SetValue(FontFamilyProperty, value); }
        }

        public double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }

        public FontStretch FontStretch
        {
            get { return (FontStretch)GetValue(FontStretchProperty); }
            set { SetValue(FontStretchProperty, value); }
        }

        public FontStyle FontStyle
        {
            get { return (FontStyle)GetValue(FontStyleProperty); }
            set { SetValue(FontStyleProperty, value); }
        }

        public FontWeight FontWeight
        {
            get { return (FontWeight)GetValue(FontWeightProperty); }
            set { SetValue(FontWeightProperty, value); }
        }

        internal void Reset()
        {
            this.SetCurrentValue(FontFamilyProperty, SystemFonts.MessageFontFamily);
            this.SetCurrentValue(FontSizeProperty, SystemFonts.MessageFontSize);
            this.SetCurrentValue(FontStretchProperty, TextElement.FontStretchProperty.DefaultMetadata.DefaultValue);
            this.SetCurrentValue(FontStyleProperty, SystemFonts.MessageFontStyle);
            this.SetCurrentValue(FontWeightProperty, SystemFonts.MessageFontWeight);
        }

        internal NativeMethods.LOGFONT ToLogFont()
        {
            NativeMethods.LOGFONT lf = new NativeMethods.LOGFONT();
            lf.lfFaceName = this.FontFamily.ToString();

            return lf;
        }

        internal void Update(NativeMethods.LOGFONT lf)
        {
            this.SetCurrentValue(FontFamilyProperty, new FontFamily(lf.lfFaceName));
            this.SetCurrentValue(FontSizeProperty, lf.lfWidth);
            this.SetCurrentValue(FontStretchProperty, TextElement.FontStretchProperty.DefaultMetadata.DefaultValue);
            this.SetCurrentValue(FontStyleProperty, SystemFonts.MessageFontStyle);
            this.SetCurrentValue(FontWeightProperty, SystemFonts.MessageFontWeight);
        }
    }
}
